package net.tv90.weatherserveradmin.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.tv90.weatherserveradmin.dao.WeatherStationDataRepository;
import net.tv90.weatherserveradmin.model.domain.WeatherStationData;
import net.tv90.weatherserveradmin.model.dto.NationalWeatherDataPrefabDTO;
import net.tv90.weatherserveradmin.model.dto.WeatherStationDataDTO;
import net.tv90.weatherserveradmin.model.dto.WeatherStationDataPrefabDTO;
import net.tv90.weatherserveradmin.utils.RedisUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WeatherStationService {

    public static final String ELEMENTS =
            "Station_Id_C,Year,Mon,Day,Hour,PRS,PRS_Sea,PRS_Max,PRS_Min,TEM,TEM_MAX,TEM_MIN,RHU,RHU_Min,VAP,PRE_3h,WIN_D_INST_Max,WIN_S_MAX,WIN_D_S_Max,WIN_S_Avg_2mi,WIN_D_Avg_2mi,WEP_Now,WIN_S_Inst_Max,VIS,CLO_Cov,CLO_COV_Low,CLO_COV_LM,Datetime";
    public static final String ELEMENTS_PREFAB =
            "Station_Id_C,Year,Mon,Day,Hour,PRS,TEM,PRE_3h";
    private static final ExecutorService executorService = Executors.newFixedThreadPool(5);
    private static final String API_BASE_URL = "http://api.data.cma.cn:8090/api";
    private static final String DATA_CODE = "SURF_CHN_MUL_HOR_3H";
    @Value("${weather.api.userid}")
    private String userId;
    @Value("${weather.api.password}")
    private String password;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private WeatherStationDataRepository weatherStationDataRepository;

    /**
     * 获取站点预览信息
     * @param stationIDs 站点号码
     * @param start 开始日期
     * @param end 结束日期
     * @return 站点信息
     */
    private List<WeatherStationDataDTO> getWeatherStationDataPrefab(String stationIDs, LocalDateTime start, LocalDateTime end) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

        String timeRange = String.format("[%s,%s]", start.format(formatter), end.format(formatter));
        String requestUrl = String.format(
                "%s?userId=%s&pwd=%s&dataFormat=json&interfaceId=getSurfEleByTimeRangeAndStaID&dataCode=%s&timeRange=%s&staIDs=%s&elements=%s",
                API_BASE_URL, userId, password, DATA_CODE, timeRange, stationIDs, ELEMENTS_PREFAB
        );

        log.info("正在请求：" + requestUrl);

        // 向中国气象网API发请求
        try {
            String response = restTemplate.getForObject(requestUrl, String.class);
            if (response == null || response.isEmpty()) {
                throw new RuntimeException("API 返回为空");
            }

            JsonNode rootNode = objectMapper.readTree(response);
            if (!"0".equals(rootNode.path("returnCode").asText())) {
                String errorMessage = rootNode.path("returnMessage").asText();
                throw new RuntimeException("API 请求失败: " + errorMessage);
            }

            JsonNode dataNode = rootNode.path("DS");
            if (!dataNode.isArray() || dataNode.size() == 0) {
                throw new RuntimeException("API 没有返回有效的数据");
            }
            return objectMapper.convertValue(dataNode, new TypeReference<>() {
            });
        } catch (Exception e) {
            // 捕获API请求异常，并尝试从数据库中获取数据
            log.error("API 请求失败，正在尝试从数据库获取数据: " + e.getMessage(), e);

            // 从数据库获取最新的气象数据
            List<WeatherStationDataDTO> fallbackData = getLatestWeatherDataFromDatabase(stationIDs, 7);

            if (fallbackData != null && !fallbackData.isEmpty()) {
                return fallbackData;
            } else {
                throw new RuntimeException("无法获取气象数据: " + e.getMessage(), e);
            }
        }
    }

    /**
     * 获取气象站某一时刻过去n天的数据预览
     * @param stationIDs
     * @param pastDays
     * @param time
     * @return
     */
    public List<WeatherStationDataDTO> getWeatherStationDataPrefab(String stationIDs, int pastDays, LocalDateTime time) {
        LocalDateTime startDate = time.minusDays(pastDays - 1);
        return getWeatherStationDataPrefab(stationIDs, startDate, time);
    }

    /**
     * 获取从现在起过去n天的站点数据预览
     * @param stationIDs
     * @param pastDays
     * @return
     */
    public List<WeatherStationDataDTO> getWeatherStationDataPrefab(String stationIDs, int pastDays) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startDate = now.minusDays(pastDays - 1);
        return getWeatherStationDataPrefab(stationIDs, startDate, now);
    }

    /**
     * 获取气象站完整信息
     * @param stationIDs
     * @param pastDays
     * @return
     */
    public List<WeatherStationDataDTO> getWeatherStationData(String stationIDs, int pastDays) {
        // 生成Redis的Key
        String redisKey = String.format("weather:data:%s:%d", stationIDs, pastDays);

        // 尝试从Redis缓存中获取数据
        List<WeatherStationDataDTO> cachedData = redisUtils.getObject(redisKey);
        if (cachedData != null) {
            return cachedData;
        }

        // 计算时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startDate = now.minusDays(pastDays - 1);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

        String timeRange = String.format("[%s,%s]", startDate.format(formatter), now.format(formatter));
        String requestUrl = String.format(
                "%s?userId=%s&pwd=%s&dataFormat=json&interfaceId=getSurfEleByTimeRangeAndStaID&dataCode=%s&timeRange=%s&staIDs=%s&elements=%s",
                API_BASE_URL, userId, password, DATA_CODE, timeRange, stationIDs, ELEMENTS
        );

        log.info("正在请求：" + requestUrl);

        // 向中国气象网API发请求
        try {
            String response = restTemplate.getForObject(requestUrl, String.class);
            if (response == null || response.isEmpty()) {
                throw new RuntimeException("API 返回为空");
            }

            JsonNode rootNode = objectMapper.readTree(response);
            if (!"0".equals(rootNode.path("returnCode").asText())) {
                String errorMessage = rootNode.path("returnMessage").asText();
                throw new RuntimeException("API 请求失败: " + errorMessage);
            }

            JsonNode dataNode = rootNode.path("DS");
            if (!dataNode.isArray() || dataNode.isEmpty()) {
                throw new RuntimeException("API 没有返回有效的数据");
            }

            List<WeatherStationDataDTO> weatherData = objectMapper.convertValue(dataNode, new TypeReference<>() {
            });

            // 将结果存入Redis，设置TTL为1小时
            redisUtils.setObject(redisKey, weatherData, 1, TimeUnit.HOURS);

            return weatherData;
        } catch (Exception e) {
            // 捕获API请求异常，并尝试从数据库中获取数据
            log.error("API 请求失败，正在尝试从数据库获取数据: " + e.getMessage(), e);

            // 从数据库获取最新的气象数据
            List<WeatherStationDataDTO> fallbackData = getLatestWeatherDataFromDatabase(stationIDs, pastDays);

            if (fallbackData != null && !fallbackData.isEmpty()) {
                return fallbackData;
            } else {
                throw new RuntimeException("无法获取气象数据: " + e.getMessage(), e);
            }
        }
    }

    /**
     * 从数据库获取最新的气象数据
     * @param stationIDs
     * @param pastDays
     * @return
     */
    private List<WeatherStationDataDTO> getLatestWeatherDataFromDatabase(String stationIDs, int pastDays) {
        // 获取数据库中最新的日期
        String latestDate = weatherStationDataRepository.findLatestDate(stationIDs);

        if (latestDate == null) {
            throw new RuntimeException("无法获取最新的日期数据");
        }

        // 将字符串转为 LocalDateTime
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime latestDateTime = LocalDateTime.parse(latestDate, formatter);

        // 计算时间范围，从最新日期往回推过去n天
        LocalDateTime startDate = latestDateTime.minusDays(pastDays - 1);  // 过去n天的开始日期

        String startDateString = startDate.format(formatter);
        String endDateString = latestDateTime.format(formatter);  // 最新日期作为结束日期

        // 使用自定义查询来获取天气数据
        List<WeatherStationData> weatherDataList = weatherStationDataRepository.findWeatherDataByDateRange(stationIDs, startDateString, endDateString);

        // 将查询结果转换为DTO列表
        return weatherDataList.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 将 WeatherStationData 转换为 WeatherStationDataDTO
     * @param data
     * @return
     */
    private WeatherStationDataDTO convertToDTO(WeatherStationData data) {
        WeatherStationDataDTO dto = new WeatherStationDataDTO();

        dto.setStationId(data.getStationId());
        dto.setYear(data.getYear());
        dto.setMonth(data.getMonth());
        dto.setDay(data.getDay());
        dto.setHour(data.getHour());
        dto.setPrs(data.getPrs());
        dto.setPrsSea(data.getPrsSea());
        dto.setPrsMax(data.getPrsMax());
        dto.setPrsMin(data.getPrsMin());
        dto.setTem(data.getTem());
        dto.setTemMax(data.getTemMax());
        dto.setTemMin(data.getTemMin());
        dto.setRhu(data.getRhu());
        dto.setRhuMin(data.getRhuMin());
        dto.setVap(data.getVap());
        dto.setPre3h(data.getPre3h());
        dto.setWinDInstMax(data.getWinDInstMax());
        dto.setWinSMax(data.getWinSMax());
        dto.setWinDSMax(data.getWinDSMax());
        dto.setWinSAvg2mi(data.getWinSAvg2mi());
        dto.setWinDAvg2mi(data.getWinDAvg2mi());
        dto.setWepNow(data.getWepNow());
        dto.setWinSInstMax(data.getWinSInstMax());
        dto.setVis(data.getVis());
        dto.setCloCov(data.getCloCov());
        dto.setCloCovLow(data.getCloCovLow());
        dto.setCloCovLm(data.getCloCovLm());
        dto.setDatetime(data.getDatetime()); // Assuming `datetime` is a LocalDateTime field

        return dto;
    }


    /**
     * 获取某时刻全国气象站预览信息
     * @return
     */
    public NationalWeatherDataPrefabDTO getNationalWeatherDataPrefab(LocalDateTime time) {

        // 检查时间差是否超过7天
        LocalDateTime currentTime = LocalDateTime.now();
        if (Duration.between(time, currentTime).toDays() > 7) {
            throw new IllegalArgumentException("请求的时间不能超过当前时间7天。");
        }

        List<Future<List<WeatherStationDataDTO>>> futures = new ArrayList<>();
        List<String> stationIds = new ArrayList<>();

        try {
            File file = new ClassPathResource("data/基本站.csv").getFile();
            CsvSchema schema = CsvSchema.builder().setUseHeader(true).build();
            CsvMapper csvMapper = new CsvMapper();
            MappingIterator<JsonNode> iterator = csvMapper.readerFor(JsonNode.class).with(schema).readValues(file);
            while (iterator.hasNext()) {
                JsonNode node = iterator.next();
                JsonNode stationIdNode = node.get("区站号");
                if (stationIdNode != null && !stationIdNode.isNull()) {
                    String stationId = stationIdNode.asText();
                    stationIds.add(stationId);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("无法读取站点信息：" + e.getMessage(), e);
        }

        // 检查缓存
//        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHH");
//        String redisKey = "weather:data:national:prefab:" + time.format(formatter);
//        NationalWeatherDataPrefabDTO cachedData = redisUtils.getObject(redisKey);
//        if (cachedData != null) {
//            return cachedData;
//        }

        List<WeatherStationDataPrefabDTO> weatherStationDataPrefabDTOS;
        // 将站点 ID 分组，每组 30 个
        int batchSize = 1;
        for (int i = 0; i < stationIds.size(); i += batchSize) {
            List<String> batch = stationIds.subList(i, Math.min(i + batchSize, stationIds.size()));
            String stationIDs = String.join(",", batch).replaceAll("\s+", "");
            // 提交多线程任务并增加异常重发机制
            Future<List<WeatherStationDataDTO>> future = executorService.submit(() -> {
                int retryCount = 3;
                while (retryCount > 0) {
                    try {
                        return getWeatherStationDataPrefab(stationIDs, 2, time);
                    } catch (Exception e) {
                        log.warn("尝试重发请求：" + stationIDs);
                        retryCount--;
                        if (retryCount == 0) {
                            throw new RuntimeException("获取站点 ID 数据失败：" + stationIDs, e);
                        }
                        try {
                            Thread.sleep(1000); // 重试前等待 1 秒
                        } catch (InterruptedException interruptedException) {
                            Thread.currentThread().interrupt();
                            throw new RuntimeException("线程中断", interruptedException);
                        }
                    }
                }
                throw new RuntimeException("请求发生错误");
            });

            try {
                Thread.sleep(125); // 每秒最多发送8个请求，间隔125毫秒
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("线程中断", e);
            }
            futures.add(future);
        }

        // 收集结果
        weatherStationDataPrefabDTOS = new ArrayList<>();
        for (Future<List<WeatherStationDataDTO>> future : futures) {
            try {
                List<WeatherStationDataDTO> weatherDataList = future.get();
                for (WeatherStationDataDTO weatherData : weatherDataList) {
                    WeatherStationDataPrefabDTO prefab = new WeatherStationDataPrefabDTO();
                    prefab.setStationId(weatherData.getStationId());
                    prefab.setYear(weatherData.getYear());
                    prefab.setMonth(weatherData.getMonth());
                    prefab.setDay(weatherData.getDay());
                    prefab.setHour(weatherData.getHour());
                    prefab.setPrs(weatherData.getPrs());
                    prefab.setTem(weatherData.getTem());
                    prefab.setPre3h(weatherData.getPre3h());
                    weatherStationDataPrefabDTOS.add(prefab);
                }
            } catch (InterruptedException | ExecutionException e) {
                log.error("无法获取气象数据：" + e.getMessage(), e);
            }

        }

        // 构建返回结果
        NationalWeatherDataPrefabDTO result = new NationalWeatherDataPrefabDTO();
        result.setWeatherStationDataPrefabDTO(weatherStationDataPrefabDTOS);

        // 将结果存入 Redis，设置缓存时间为 1 小时
//        redisUtils.setObject(redisKey, result, 1, TimeUnit.HOURS);

        return result;

    }

    /**
     * 获取当前时刻全国气象站预览信息
     * @return
     */
    public NationalWeatherDataPrefabDTO getNationalWeatherDataPrefab() {
        return getNationalWeatherDataPrefab(LocalDateTime.now());
    }


    /**
     * 定时抓取中国气象网的基本站信息
     */
    @Scheduled(fixedRate =  6 * 24 * 60 * 60 * 1000)
    private void getNationalWeatherDataAndSave() {

        List<Future<List<WeatherStationDataDTO>>> futures = new ArrayList<>();
        List<String> stationIds = new ArrayList<>();

        try {
            File file = new ClassPathResource("data/基本站.csv").getFile();
            CsvSchema schema = CsvSchema.builder().setUseHeader(true).build();
            CsvMapper csvMapper = new CsvMapper();
            MappingIterator<JsonNode> iterator = csvMapper.readerFor(JsonNode.class).with(schema).readValues(file);
            while (iterator.hasNext()) {
                JsonNode node = iterator.next();
                JsonNode stationIdNode = node.get("区站号");
                if (stationIdNode != null && !stationIdNode.isNull()) {
                    String stationId = stationIdNode.asText();
                    stationIds.add(stationId);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException("无法读取站点信息：" + e.getMessage(), e);
        }

        // 将站点 ID 分组，每组 30 个
        int batchSize = 30;
        for (int i = 0; i < stationIds.size(); i += batchSize) {
            List<String> batch = stationIds.subList(i, Math.min(i + batchSize, stationIds.size()));
            String stationIDs = String.join(",", batch).replaceAll("\s+", "");
            // 提交多线程任务并增加异常重发机制
            Future<List<WeatherStationDataDTO>> future = executorService.submit(() -> {
                int retryCount = 3;
                while (retryCount > 0) {
                    try {
                        return getWeatherStationData(stationIDs, 7);
                    } catch (Exception e) {
                        log.warn("尝试重发请求：" + stationIDs);
                        retryCount--;
                        if (retryCount == 0) {
                            throw new RuntimeException("获取站点 ID 数据失败：" + stationIDs, e);
                        }
                        try {
                            Thread.sleep(1000); // 重试前等待 1 秒
                        } catch (InterruptedException interruptedException) {
                            Thread.currentThread().interrupt();
                            throw new RuntimeException("线程中断", interruptedException);
                        }
                    }
                }
                throw new RuntimeException("请求发生错误");
            });

            try {
                Thread.sleep(125); // 每秒最多发送8个请求，间隔125毫秒
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("线程中断", e);
            }
            futures.add(future);
        }

        // 收集结果
        Set<String> existingKeys = new ConcurrentSkipListSet<>();
        List<WeatherStationData> result = new ArrayList<>();

        // 检查现有数据以避免重复插入（基于区站号和时间）
        for (WeatherStationData data : weatherStationDataRepository.findAll()) {
            String key = data.getStationId() + "-" + data.getYear() + "-" + data.getMonth() + "-" + data.getDay() + "-" + data.getHour();
            existingKeys.add(key);
        }

        for (Future<List<WeatherStationDataDTO>> future : futures) {
            try {
                List<WeatherStationDataDTO> weatherDataList = future.get();
                for (WeatherStationDataDTO stationDataDTO : weatherDataList) {
                    String key = stationDataDTO.getStationId() + "-" + stationDataDTO.getYear() + "-" + stationDataDTO.getMonth() + "-" + stationDataDTO.getDay() + "-" + stationDataDTO.getHour();
                    if (!existingKeys.contains(key)) {
                        WeatherStationData data = new WeatherStationData();
                        BeanUtils.copyProperties(stationDataDTO, data);
                        result.add(data);
                    } else {
                        log.warn("重复的站点数据被跳过: " + stationDataDTO.getStationId() + " 时间: " + stationDataDTO.getYear() + "-" + stationDataDTO.getMonth() + "-" + stationDataDTO.getDay() + " " + stationDataDTO.getHour() + ":00");
                    }
                }
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
                throw new RuntimeException("无法获取气象数据：" + e.getMessage(), e);
            }
        }

        // 使用 Set 来追踪 stationId 和时间的唯一组合，防止 result 列表中存在重复的数据
        Set<String> uniqueKeys = new HashSet<>();
        List<WeatherStationData> uniqueResult = new ArrayList<>();

        for (WeatherStationData data : result) {
            String key = data.getStationId() + "-" + data.getYear() + "-" + data.getMonth() + "-" + data.getDay() + "-" + data.getHour();
            if (!uniqueKeys.contains(key)) {
                uniqueResult.add(data);
                uniqueKeys.add(key); // 将键加入到 Set 中，确保唯一性
            } else {
                log.warn("重复的站点数据在最终结果中被过滤掉: " + data.getStationId() + " 时间: " + data.getYear() + "-" + data.getMonth() + "-" + data.getDay() + " " + data.getHour() + ":00");
            }
        }

        // 只保存新的数据
        if (!uniqueResult.isEmpty()) {
            weatherStationDataRepository.saveAll(uniqueResult);
        } else {
            log.info("没有新的气象数据需要保存。");
        }

    }


    public int ifRainTomorrow(WeatherStationDataDTO record, List<WeatherStationDataDTO> data) {
        LocalDate tomorrow = LocalDate.of(record.getYear(), record.getMonth(), record.getDay()).plusDays(1);
        boolean rain = false;
        boolean foundTomorrow = false;
        for (WeatherStationDataDTO entry : data) {
            if (entry.getYear() == tomorrow.getYear() &&
                    entry.getMonth() == tomorrow.getMonthValue() &&
                    entry.getDay() == tomorrow.getDayOfMonth()) {
                foundTomorrow = true;
                if (entry.getPre3h() > 0) {
                    rain = true;
                    break;
                }
            }
        }
        if (!foundTomorrow) {
            return -1;
        }
        return rain ? 1 : 0;
    }


}
